home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / RCS_56.ARJ / PARTIME.C < prev    next >
C/C++ Source or Header  |  1992-02-11  |  19KB  |  640 lines

  1. /*
  2.  * PARTIME        parse date/time string into a TM structure
  3.  *
  4.  * Returns:
  5.  *    0 if parsing failed
  6.  *    else time values in specified TM structure and zone (unspecified values
  7.  *        set to TMNULL)
  8.  * Notes:
  9.  *    This code is quasi-public; it may be used freely in like software.
  10.  *    It is not to be sold, nor used in licensed software without
  11.  *    permission of the author.
  12.  *    For everyone's benefit, please report bugs and improvements!
  13.  *     Copyright 1980 by Ken Harrenstien, SRI International.
  14.  *    (ARPANET: KLH @ SRI)
  15.  */
  16.  
  17. /* Hacknotes:
  18.  *    If parsing changed so that no backup needed, could perhaps modify
  19.  *        to use a FILE input stream.  Need terminator, though.
  20.  *    Perhaps should return 0 on success, else a non-zero error val?
  21.  */
  22.  
  23. /* $Log: partime.c,v $
  24.  * Revision 5.6  1991/08/19  03:13:55  eggert
  25.  * Update timezones.
  26.  *
  27.  * Revision 5.5  1991/04/21  11:58:18  eggert
  28.  * Don't put , just before } in initializer.
  29.  *
  30.  * Revision 5.4  1990/10/04  06:30:15  eggert
  31.  * Remove date vs time heuristics that fail between 2000 and 2400.
  32.  * Check for overflow when lexing an integer.
  33.  * Parse 'Jan 10 LT' as 'Jan 10, LT', not 'Jan, 10 LT'.
  34.  *
  35.  * Revision 5.3  1990/09/24  18:56:31  eggert
  36.  * Update timezones.
  37.  *
  38.  * Revision 5.2  1990/09/04  08:02:16  eggert
  39.  * Don't parse two-digit years, because it won't work after 1999/12/31.
  40.  * Don't permit 'Aug Aug'.
  41.  *
  42.  * Revision 5.1  1990/08/29  07:13:49  eggert
  43.  * Be able to parse our own date format.  Don't assume year<10000.
  44.  *
  45.  * Revision 5.0  1990/08/22  08:12:40  eggert
  46.  * Switch to GMT and fix the bugs exposed thereby.  Update timezones.
  47.  * Ansify and Posixate.  Fix peekahead and int-size bugs.
  48.  *
  49.  * Revision 1.4  89/05/01  14:48:46  narten
  50.  * fixed #ifdef DEBUG construct
  51.  * 
  52.  * Revision 1.3  88/08/28  14:53:40  eggert
  53.  * Remove unportable "#endif XXX"s.
  54.  * 
  55.  * Revision 1.2  87/03/27  14:21:53  jenkins
  56.  * Port to suns
  57.  * 
  58.  * Revision 1.1  82/05/06  11:38:26  wft
  59.  * Initial revision
  60.  * 
  61.  */
  62.  
  63. #include "rcsbase.h"
  64.  
  65. libId(partId, "$Id: partime.c,v 5.6 1991/08/19 03:13:55 eggert Exp $")
  66.  
  67. #define given(v) (0 <= (v))
  68. #define TMNULL (-1) /* Items not given are given this value */
  69. #define TZ_OFFSET (24*60) /* TMNULL  <  zone_offset - TZ_OFFSET */
  70.  
  71. struct tmwent {
  72.     char const *went;
  73.     short wval;
  74.     char wflgs;
  75.     char wtype;
  76. };
  77.     /* wflgs */
  78. #define TWTIME 02    /* Word is a time value (absence implies date) */
  79. #define TWDST  04    /* Word is a DST-type timezone */
  80.     /* wtype */
  81. #define TM_MON    1    /* month name */
  82. #define TM_WDAY    2    /* weekday name */
  83. #define TM_ZON    3    /* time zone name */
  84. #define TM_LT    4    /* local time */
  85. #define TM_DST    5    /* daylight savings time */
  86. #define TM_12    6    /* AM, PM, NOON, or MIDNIGHT */
  87.     /* wval (for wtype==TM_12) */
  88. #define T12_AM 1
  89. #define T12_PM 2
  90. #define T12_NOON 12
  91. #define T12_MIDNIGHT 0
  92.  
  93. static struct tmwent const tmwords [] = {
  94.     {"january",      0, 0, TM_MON},
  95.     {"february",     1, 0, TM_MON},
  96.     {"march",        2, 0, TM_MON},
  97.     {"april",        3, 0, TM_MON},
  98.     {"may",          4, 0, TM_MON},
  99.     {"june",         5, 0, TM_MON},
  100.     {"july",         6, 0, TM_MON},
  101.     {"august",       7, 0, TM_MON},
  102.     {"september",    8, 0, TM_MON},
  103.     {"october",      9, 0, TM_MON},
  104.     {"november",     10, 0, TM_MON},
  105.     {"december",     11, 0, TM_MON},
  106.  
  107.     {"sunday",       0, 0, TM_WDAY},
  108.     {"monday",       1, 0, TM_WDAY},
  109.     {"tuesday",      2, 0, TM_WDAY},
  110.     {"wednesday",    3, 0, TM_WDAY},
  111.     {"thursday",     4, 0, TM_WDAY},
  112.     {"friday",       5, 0, TM_WDAY},
  113.     {"saturday",     6, 0, TM_WDAY},
  114.  
  115.     {"gmt",          0*60, TWTIME, TM_ZON},   /* Greenwich */
  116.     {"utc",          0*60, TWTIME, TM_ZON},
  117.     {"ut",           0*60, TWTIME, TM_ZON},
  118.     {"cut",         0*60, TWTIME, TM_ZON},
  119.  
  120.     {"nzst",        -12*60, TWTIME, TM_ZON},  /* New Zealand */
  121.     {"jst",         -9*60, TWTIME, TM_ZON},   /* Japan */
  122.     {"kst",         -9*60, TWTIME, TM_ZON},   /* Korea */
  123.     {"ist",         -5*60-30, TWTIME, TM_ZON},/* India */
  124.     {"eet",         -2*60, TWTIME, TM_ZON},   /* Eastern Europe */
  125.     {"cet",         -1*60, TWTIME, TM_ZON},   /* Central Europe */
  126.     {"met",         -1*60, TWTIME, TM_ZON},   /* Middle Europe */
  127.     {"wet",          0*60, TWTIME, TM_ZON},   /* Western Europe */
  128.     {"nst",          3*60+30, TWTIME, TM_ZON},/* Newfoundland */
  129.     {"ast",          4*60, TWTIME, TM_ZON},   /* Atlantic */
  130.     {"est",          5*60, TWTIME, TM_ZON},   /* Eastern */
  131.     {"cst",          6*60, TWTIME, TM_ZON},   /* Central */
  132.     {"mst",          7*60, TWTIME, TM_ZON},   /* Mountain */
  133.     {"pst",          8*60, TWTIME, TM_ZON},   /* Pacific */
  134.     {"akst",         9*60, TWTIME, TM_ZON},   /* Alaska */
  135.     {"hast",         10*60, TWTIME, TM_ZON},  /* Hawaii-Aleutian */
  136.     {"hst",          10*60, TWTIME, TM_ZON},  /* Hawaii */
  137.     {"sst",          11*60, TWTIME, TM_ZON},  /* Samoa */
  138.  
  139.     {"nzdt",        -12*60, TWTIME+TWDST, TM_ZON},    /* New Zealand */
  140.     {"kdt",         -9*60, TWTIME+TWDST, TM_ZON},     /* Korea */
  141.     {"bst",          0*60, TWTIME+TWDST, TM_ZON},     /* Britain */
  142.     {"ndt",         3*60+30, TWTIME+TWDST, TM_ZON},  /* Newfoundland */
  143.     {"adt",          4*60, TWTIME+TWDST, TM_ZON},     /* Atlantic */
  144.     {"edt",          5*60, TWTIME+TWDST, TM_ZON},     /* Eastern */
  145.     {"cdt",          6*60, TWTIME+TWDST, TM_ZON},     /* Central */
  146.     {"mdt",          7*60, TWTIME+TWDST, TM_ZON},     /* Mountain */
  147.     {"pdt",          8*60, TWTIME+TWDST, TM_ZON},     /* Pacific */
  148.     {"akdt",         9*60, TWTIME+TWDST, TM_ZON},     /* Alaska */
  149.     {"hadt",         10*60, TWTIME+TWDST, TM_ZON},    /* Hawaii-Aleutian */
  150.  
  151. #if 0
  152.     /*
  153.      * The following names are duplicates or are not well attested.
  154.      * A standard is needed.
  155.      */
  156.     {"east",        -10*60, TWTIME, TM_ZON},  /* Eastern Australia */
  157.     {"cast",        -9*60-30, TWTIME, TM_ZON},/* Central Australia */
  158.     {"cst",         -8*60, TWTIME, TM_ZON},   /* China */
  159.     {"hkt",         -8*60, TWTIME, TM_ZON},   /* Hong Kong */
  160.     {"sst",         -8*60, TWTIME, TM_ZON},   /* Singapore */
  161.     {"wast",        -8*60, TWTIME, TM_ZON},   /* Western Australia */
  162.     {"?",        -6*60-30, TWTIME, TM_ZON},/* Burma */
  163.     {"?",           -4*60-30, TWTIME, TM_ZON},/* Afghanistan */
  164.     {"it",          -3*60-30, TWTIME, TM_ZON},/* Iran */
  165.     {"ist",         -2*60, TWTIME, TM_ZON},   /* Israel */
  166.     {"mez",        -1*60, TWTIME, TM_ZON},   /* Mittel-Europaeische Zeit */
  167.     {"ast",          1*60, TWTIME, TM_ZON},   /* Azores */
  168.     {"fst",          2*60, TWTIME, TM_ZON},   /* Fernando de Noronha */
  169.     {"bst",          3*60, TWTIME, TM_ZON},   /* Brazil */
  170.     {"wst",          4*60, TWTIME, TM_ZON},   /* Western Brazil */
  171.     {"ast",          5*60, TWTIME, TM_ZON},   /* Acre Brazil */
  172.     {"?",            9*60+30, TWTIME, TM_ZON},/* Marquesas */
  173.     {"?",         12*60, TWTIME, TM_ZON},  /* Kwajalein */
  174.  
  175.     {"eadt",        -10*60, TWTIME+TWDST, TM_ZON},    /* Eastern Australia */
  176.     {"cadt",        -9*60-30, TWTIME+TWDST, TM_ZON},  /* Central Australia */
  177.     {"cdt",         -8*60, TWTIME+TWDST, TM_ZON},     /* China */
  178.     {"wadt",        -8*60, TWTIME+TWDST, TM_ZON},     /* Western Australia */
  179.     {"idt",         -2*60, TWTIME+TWDST, TM_ZON},     /* Israel */
  180.     {"eest",        -2*60, TWTIME+TWDST, TM_ZON},     /* Eastern Europe */
  181.     {"cest",        -1*60, TWTIME+TWDST, TM_ZON},     /* Central Europe */
  182.     {"mest",        -1*60, TWTIME+TWDST, TM_ZON},     /* Middle Europe */
  183.     {"mesz",    -1*60, TWTIME+TWDST, TM_ZON},      /* Mittel-Europaeische Sommerzeit */
  184.     {"west",         0*60, TWTIME+TWDST, TM_ZON},     /* Western Europe */
  185.     {"adt",          1*60, TWTIME+TWDST, TM_ZON},      /* Azores */
  186.     {"fdt",          2*60, TWTIME+TWDST, TM_ZON},     /* Fernando de Noronha */
  187.     {"edt",          3*60, TWTIME+TWDST, TM_ZON},     /* Eastern Brazil */
  188.     {"wdt",          4*60, TWTIME+TWDST, TM_ZON},     /* Western Brazil */
  189.     {"adt",          5*60, TWTIME+TWDST, TM_ZON},     /* Acre Brazil */
  190. #endif
  191.  
  192.     {"lt",           0, TWTIME, TM_LT},       /* local time */
  193.     {"dst",          1*60, TWTIME, TM_DST},      /* daylight savings time */
  194.     {"ddst",         2*60, TWTIME, TM_DST},      /* double dst */
  195.  
  196.     {"am",           T12_AM,    TWTIME, TM_12},
  197.     {"pm",           T12_PM,    TWTIME, TM_12},
  198.     {"noon",         T12_NOON,    TWTIME, TM_12},
  199.     {"midnight",     T12_MIDNIGHT,    TWTIME, TM_12},
  200.  
  201.     {0, 0, 0, 0}    /* Zero entry to terminate searches */
  202. };
  203.  
  204. struct token {
  205.     char const *tcp;/* pointer to string */
  206.     int tcnt;    /* # chars */
  207.     char tbrk;    /* "break" char */
  208.     char tbrkl;    /* last break char */
  209.     char tflg;    /* 0 = alpha, 1 = numeric */
  210.     union {         /* Resulting value; */
  211.         int tnum;/* either a #, or */
  212.         struct tmwent const *ttmw;/* a ptr to a tmwent.  */
  213.     } tval;
  214. };
  215.  
  216. static struct tmwent const*ptmatchstr P((char const*,int,struct tmwent const*));
  217. static int pt12hack P((struct tm *,int));
  218. static int ptitoken P((struct token *));
  219. static int ptstash P((int *,int));
  220. static int pttoken P((struct token *));
  221.  
  222.     static int
  223. goodzone(t, offset, am)
  224.     register struct token const *t;
  225.     int offset;
  226.     int *am;
  227. {
  228.     register int m;
  229.     if (
  230.         t->tflg  &&
  231.         t->tcnt == 4+offset  &&
  232.         (m = t->tval.tnum) <= 2400  &&
  233.         isdigit(t->tcp[offset]) &&
  234.         (m%=100) < 60
  235.     ) {
  236.         m += t->tval.tnum/100 * 60;
  237.         if (t->tcp[offset-1]=='+')
  238.             m = -m;
  239.         *am = m;
  240.         return 1;
  241.     }
  242.     return 0;
  243. }
  244.  
  245.     int
  246. partime(astr, atm, zone)
  247. char const *astr;
  248. register struct tm *atm;
  249. int *zone;
  250. {
  251.     register int i;
  252.     struct token btoken, atoken;
  253.     int zone_offset; /* minutes west of GMT, plus TZ_OFFSET */
  254.     register char const *cp;
  255.     register char ch;
  256.     int ord, midnoon;
  257.     int *atmfield, dst, m;
  258.     int got1 = 0;
  259.  
  260.     atm->tm_sec = TMNULL;
  261.     atm->tm_min = TMNULL;
  262.     atm->tm_hour = TMNULL;
  263.     atm->tm_mday = TMNULL;
  264.     atm->tm_mon = TMNULL;
  265.     atm->tm_year = TMNULL;
  266.     atm->tm_wday = TMNULL;
  267.     atm->tm_yday = TMNULL;
  268.     midnoon = TMNULL;        /* and our own temp stuff */
  269.     zone_offset = TMNULL;
  270.     dst = TMNULL;
  271.     btoken.tcnt = btoken.tbrk = 0;
  272.     btoken.tcp = astr;
  273.  
  274.     for (;; got1=1) {
  275.     if (!ptitoken(&btoken))                /* Get a token */
  276.       {     if(btoken.tval.tnum) return(0);         /* Read error? */
  277.         if (given(midnoon))            /* EOF, wrap up */
  278.             if (!pt12hack(atm, midnoon))
  279.                 return 0;
  280.         if (!given(atm->tm_min))
  281.             atm->tm_min = 0;
  282.         *zone  =
  283.                 (given(zone_offset) ? zone_offset-TZ_OFFSET : 0)
  284.             -    (given(dst) ? dst : 0);
  285.         return got1;
  286.       }
  287.     if(btoken.tflg == 0)        /* Alpha? */
  288.       {     i = btoken.tval.ttmw->wval;
  289.         switch (btoken.tval.ttmw->wtype) {
  290.           default:
  291.             return 0;
  292.           case TM_MON:
  293.             atmfield = &atm->tm_mon;
  294.             break;
  295.           case TM_WDAY:
  296.             atmfield = &atm->tm_wday;
  297.             break;
  298.           case TM_DST:
  299.             atmfield = &dst;
  300.             break;
  301.           case TM_LT:
  302.             if (ptstash(&dst, 0))
  303.                 return 0;
  304.             i = 48*60; /* local time magic number -- see maketime() */
  305.             /* fall into */
  306.           case TM_ZON:
  307.             i += TZ_OFFSET;
  308.             if (btoken.tval.ttmw->wflgs & TWDST)
  309.                 if (ptstash(&dst, 60))
  310.                     return 0;
  311.             /* Peek ahead for offset immediately afterwards. */
  312.             if (
  313.                 (btoken.tbrk=='-' || btoken.tbrk=='+') &&
  314.                 (atoken=btoken, ++atoken.tcnt, ptitoken(&atoken)) &&
  315.                 goodzone(&atoken, 0, &m)
  316.             ) {
  317.                 i += m;
  318.                 btoken = atoken;
  319.             }
  320.             atmfield = &zone_offset;
  321.             break;
  322.           case TM_12:
  323.             atmfield = &midnoon;
  324.         }
  325.         if (ptstash(atmfield, i))
  326.             return(0);        /* ERR: val already set */
  327.         continue;
  328.       }
  329.  
  330.     /* Token is number.  Lots of hairy heuristics. */
  331.     if (!isdigit(*btoken.tcp)) {
  332.         if (!goodzone(&btoken, 1, &m))
  333.             return 0;
  334.         zone_offset = TZ_OFFSET + m;
  335.         continue;
  336.     }
  337.  
  338.     i = btoken.tval.tnum;   /* Value now known to be valid; get it. */
  339.     if (btoken.tcnt == 3)    /*  3 digits = HMM   */
  340.       {
  341. hhmm4:        if (ptstash(&atm->tm_min, i%100))
  342.             return(0);        /* ERR: min conflict */
  343.         i /= 100;
  344. hh2:            if (ptstash(&atm->tm_hour, i))
  345.             return(0);        /* ERR: hour conflict */
  346.         continue;
  347.       }
  348.  
  349.     if (4 < btoken.tcnt)
  350.         goto year4; /* far in the future */
  351.     if(btoken.tcnt == 4)    /* 4 digits = YEAR or HHMM */
  352.       {    if (given(atm->tm_year)) goto hhmm4;    /* Already got yr? */
  353.         if (given(atm->tm_hour)) goto year4;    /* Already got hr? */
  354.         if(btoken.tbrk == ':')            /* HHMM:SS ? */
  355.             if ( ptstash(&atm->tm_hour, i/100)
  356.               || ptstash(&atm->tm_min, i%100))
  357.                 return(0);        /* ERR: hr/min clash */
  358.             else goto coltm2;        /* Go handle SS */
  359.         if(btoken.tbrk != ',' && btoken.tbrk != '/'
  360.           && (atoken=btoken, ptitoken(&atoken))    /* Peek */
  361.           && ( atoken.tflg
  362.              ? !isdigit(*atoken.tcp)
  363.              : atoken.tval.ttmw->wflgs & TWTIME)) /* HHMM-ZON */
  364.             goto hhmm4;
  365.         goto year4;            /* Give up, assume year. */
  366.       }
  367.  
  368.     /* From this point on, assume tcnt == 1 or 2 */
  369.     /* 2 digits = MM, DD, or HH (MM and SS caught at coltime) */
  370.     if(btoken.tbrk == ':')        /* HH:MM[:SS] */
  371.         goto coltime;        /*  must be part of time. */
  372.     if (31 < i)
  373.         return 0;
  374.  
  375.     /* Check for numerical-format date */
  376.     for (cp = "/-."; ch = *cp++;)
  377.       {    ord = (ch == '.' ? 0 : 1);    /* n/m = D/M or M/D */
  378.         if(btoken.tbrk == ch)            /* "NN-" */
  379.           {    if(btoken.tbrkl != ch)
  380.               {
  381.                 atoken = btoken;
  382.                 atoken.tcnt++;
  383.                 if (ptitoken(&atoken)
  384.                   && atoken.tflg == 0
  385.                   && atoken.tval.ttmw->wtype == TM_MON)
  386.                     goto dd2;
  387.                 if(ord)goto mm2; else goto dd2; /* "NN-" */
  388.               }                /* "-NN-" */
  389.             if (!given(atm->tm_mday)
  390.               && given(atm->tm_year))    /* If "YYYY-NN-" */
  391.                 goto mm2;        /* then always MM */
  392.             if(ord)goto dd2; else goto mm2;
  393.           }
  394.         if(btoken.tbrkl == ch            /* "-NN" */
  395.           && given(ord ? atm->tm_mon : atm->tm_mday))
  396.             if (!given(ord ? atm->tm_mday : atm->tm_mon)) /* MM/DD */
  397.                 if(ord)goto dd2; else goto mm2;
  398.       }
  399.  
  400.     /* Now reduced to choice between HH and DD */
  401.     if (given(atm->tm_hour)) goto dd2;    /* Have hour? Assume day. */
  402.     if (given(atm->tm_mday)) goto hh2;    /* Have day? Assume hour. */
  403.     if (given(atm->tm_mon)) goto dd2;    /* Have month? Assume day. */
  404.     if(i > 24) goto dd2;            /* Impossible HH means DD */
  405.     atoken = btoken;
  406.     if (!ptitoken(&atoken))            /* Read ahead! */
  407.         if(atoken.tval.tnum) return(0); /* ERR: bad token */
  408.         else goto dd2;            /* EOF, assume day. */
  409.     if ( atoken.tflg
  410.        ? !isdigit(*atoken.tcp)
  411.        : atoken.tval.ttmw->wflgs & TWTIME)
  412.         /* If next token is a time spec, assume hour */
  413.         goto hh2;        /* e.g. "3 PM", "11-EDT"  */
  414.  
  415. dd2:    if (ptstash(&atm->tm_mday, i))    /* Store day (1 based) */
  416.         return(0);
  417.     continue;
  418.  
  419. mm2:    if (ptstash(&atm->tm_mon, i-1))    /* Store month (make zero based) */
  420.         return(0);
  421.     continue;
  422.  
  423. year4:    if ((i-=1900) < 0  ||  ptstash(&atm->tm_year, i)) /* Store year-1900 */
  424.         return(0);        /* ERR: year conflict */
  425.     continue;
  426.  
  427.     /* Hack HH:MM[[:]SS] */
  428. coltime:
  429.     if (ptstash(&atm->tm_hour, i)) return 0;
  430.     if (!ptitoken(&btoken))
  431.         return(!btoken.tval.tnum);
  432.     if(!btoken.tflg) return(0);    /* ERR: HH:<alpha> */
  433.     if(btoken.tcnt == 4)        /* MMSS */
  434.         if (ptstash(&atm->tm_min, btoken.tval.tnum/100)
  435.           || ptstash(&atm->tm_sec, btoken.tval.tnum%100))
  436.             return(0);
  437.         else continue;
  438.     if(btoken.tcnt != 2
  439.       || ptstash(&atm->tm_min, btoken.tval.tnum))
  440.         return(0);        /* ERR: MM bad */
  441.     if (btoken.tbrk != ':') continue;    /* Seconds follow? */
  442. coltm2:    if (!ptitoken(&btoken))
  443.         return(!btoken.tval.tnum);
  444.     if(!btoken.tflg || btoken.tcnt != 2    /* Verify SS */
  445.       || ptstash(&atm->tm_sec, btoken.tval.tnum))
  446.         return(0);        /* ERR: SS bad */
  447.     }
  448. }
  449.  
  450. /* Store date/time value, return 0 if successful.
  451.  * Fail if entry is already set.
  452.  */
  453.     static int
  454. ptstash(adr,val)
  455. int *adr;
  456. int val;
  457. {    register int *a;
  458.     if (given(*(a=adr)))
  459.         return 1;
  460.     *a = val;
  461.     return(0);
  462. }
  463.  
  464. /* This subroutine is invoked for AM, PM, NOON and MIDNIGHT when wrapping up
  465.  * just prior to returning from partime.
  466.  */
  467.     static int
  468. pt12hack(tm, aval)
  469. register struct tm *tm;
  470. register int aval;
  471. {    register int h = tm->tm_hour;
  472.     switch (aval) {
  473.       case T12_AM:
  474.       case T12_PM:
  475.         if (h > 12)
  476.             return 0;
  477.         if (h == 12)
  478.             tm->tm_hour = 0;
  479.         if (aval == T12_PM)
  480.             tm->tm_hour += 12;
  481.         break;
  482.       default:
  483.         if (0 < tm->tm_min  ||  0 < tm->tm_sec)
  484.             return 0;
  485.         if (!given(h) || h==12)
  486.             tm->tm_hour = aval;
  487.         else if (aval==T12_MIDNIGHT  &&  (h==0 || h==24))
  488.             return 0;
  489.     }
  490.     return 1;
  491. }
  492.  
  493. /* Get a token and identify it to some degree.
  494.  * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
  495.  * hit error of some sort
  496.  */
  497.  
  498.     static int
  499. ptitoken(tkp)
  500. register struct token *tkp;
  501. {
  502.     register char const *cp;
  503.     register int i, j, k;
  504.  
  505.     if (!pttoken(tkp))
  506. #ifdef DEBUG
  507.         {
  508.         VOID printf("EOF\n");
  509.         return(0);
  510.         }
  511. #else
  512.         return(0);
  513. #endif    
  514.     cp = tkp->tcp;
  515.  
  516. #ifdef DEBUG
  517.     VOID printf("Token: \"%.*s\" ", tkp->tcnt, cp);
  518. #endif
  519.  
  520.     if (tkp->tflg) {
  521.         i = tkp->tcnt;
  522.         if (*cp == '+' || *cp == '-') {
  523.             cp++;
  524.             i--;
  525.         }
  526.         while (0 <= --i) {
  527.             j = tkp->tval.tnum*10;
  528.             k = j + (*cp++ - '0');
  529.             if (j/10 != tkp->tval.tnum  ||  k < j) {
  530.                 /* arithmetic overflow */
  531.                 tkp->tval.tnum = 1;
  532.                 return 0;
  533.             }
  534.             tkp->tval.tnum = k;
  535.         }
  536.     } else if (!(tkp->tval.ttmw  =  ptmatchstr(cp, tkp->tcnt, tmwords)))
  537.       {
  538. #ifdef DEBUG
  539.         VOID printf("Not found!\n");
  540. #endif
  541.         tkp->tval.tnum = 1;
  542.         return 0;
  543.       }
  544.  
  545. #ifdef DEBUG
  546.     if(tkp->tflg)
  547.         VOID printf("Val: %d.\n",tkp->tval.tnum);
  548.     else VOID printf("Found: \"%s\", val: %d, type %d\n",
  549.         tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
  550. #endif
  551.  
  552.     return(1);
  553. }
  554.  
  555. /* Read token from input string into token structure */
  556.     static int
  557. pttoken(tkp)
  558. register struct token *tkp;
  559. {
  560.     register char const *cp;
  561.     register int c;
  562.     char const *astr;
  563.  
  564.     tkp->tcp = astr = cp = tkp->tcp + tkp->tcnt;
  565.     tkp->tbrkl = tkp->tbrk;        /* Set "last break" */
  566.     tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
  567.     tkp->tval.tnum = 0;
  568.  
  569.     while(c = *cp++)
  570.       {    switch(c)
  571.           {    case ' ': case '\t':    /* Flush all whitespace */
  572.             case '\r': case '\n':
  573.             case '\v': case '\f':
  574.                 if (!tkp->tcnt) {    /* If no token yet */
  575.                     tkp->tcp = cp;    /* ignore the brk */
  576.                     continue;    /* and go on. */
  577.                 }
  578.                 /* fall into */
  579.             case '(': case ')':    /* Perhaps any non-alphanum */
  580.             case '-': case ',':    /* shd qualify as break? */
  581.             case '+':
  582.             case '/': case ':': case '.':    /* Break chars */
  583.                 if(tkp->tcnt == 0)    /* If no token yet */
  584.                   {    tkp->tcp = cp;    /* ignore the brk */
  585.                     tkp->tbrkl = c;
  586.                       continue;    /* and go on. */
  587.                   }
  588.                 tkp->tbrk = c;
  589.                 return(tkp->tcnt);
  590.           }
  591.         if (!tkp->tcnt++) {        /* If first char of token, */
  592.             if (isdigit(c)) {
  593.                 tkp->tflg = 1;
  594.                 if (astr<cp-2 && (cp[-2]=='-'||cp[-2]=='+')) {
  595.                     /* timezone is break+sign+digit */
  596.                     tkp->tcp--;
  597.                     tkp->tcnt++;
  598.                 }
  599.             }
  600.         } else if ((isdigit(c)!=0) != tkp->tflg) { /* else check type */
  601.             tkp->tbrk = c;
  602.             return --tkp->tcnt;    /* Wrong type, back up */
  603.         }
  604.       }
  605.     return(tkp->tcnt);        /* When hit EOF */
  606. }
  607.  
  608.  
  609.     static struct tmwent const *
  610. ptmatchstr(astr,cnt,astruc)
  611.     char const *astr;
  612.     int cnt;
  613.     struct tmwent const *astruc;
  614. {
  615.     register char const *cp, *mp;
  616.     register int c;
  617.     struct tmwent const *lastptr;
  618.     int i;
  619.  
  620.     lastptr = 0;
  621.     for(;mp = astruc->went; astruc += 1)
  622.       {    cp = astr;
  623.         for(i = cnt; i > 0; i--)
  624.           {
  625.             switch (*cp++ - (c = *mp++))
  626.               {    case 0: continue;    /* Exact match */
  627.                 case 'A'-'a':
  628.                     if (ctab[c] == Letter)
  629.                     continue;
  630.               }
  631.             break;
  632.           }
  633.         if(i==0)
  634.             if (!*mp) return astruc;    /* Exact match */
  635.             else if(lastptr) return(0);    /* Ambiguous */
  636.             else lastptr = astruc;        /* 1st ambig */
  637.       }
  638.     return lastptr;
  639. }
  640.